import * as echarts from 'echarts/core';
import type { XAXisOption, YAXisOption } from 'echarts/types/dist/shared.d.ts';
import {
  DatasetComponent,
  type DatasetComponentOption,
  GridComponent,
  type GridComponentOption,
  LegendComponent,
  type LegendComponentOption,
  TooltipComponent,
  type TooltipComponentOption,
} from 'echarts/components';
import { BarChart, type BarSeriesOption, GaugeChart, type GaugeSeriesOption, LineChart, type LineSeriesOption } from 'echarts/charts';
import { CanvasRenderer } from 'echarts/renderers';
import { hexToRgba } from '@/utils/color';

echarts.use([TooltipComponent, GridComponent, LegendComponent, DatasetComponent, LineChart, CanvasRenderer, GaugeChart, BarChart]);

type _$Location = [number, number, number, number];

type _$LinearGradient = InstanceType<typeof echarts.graphic.LinearGradient>;

type _$ComposeOption = BarSeriesOption | GaugeSeriesOption | LineSeriesOption;
type _$ECOption<T extends _$ComposeOption> = echarts.ComposeOption<
  DatasetComponentOption | GridComponentOption | LegendComponentOption | T | TooltipComponentOption
>;

/** 柱状图的配置项 */
export type _$BarECOption = _$ECOption<BarSeriesOption>;

/** 仪表盘的配置项 */
export type _$GaugeECOption = _$ECOption<GaugeSeriesOption>;

/** 折线图的配置项 */
export type _$LineECOption = _$ECOption<LineSeriesOption>;

/**
 * 得到渐变色
 */
function getLinearGradient(colors: [string, string], location: _$Location): _$LinearGradient {
  const colorStop = [
    { offset: 0, color: colors[0] },
    { offset: 1, color: colors[1] },
  ];
  return new echarts.graphic.LinearGradient(...location, colorStop);
}

const colors = ['#409eff', '#f56c6c', '#e6a23c', '#67c23a', '#fe68f2', '#00ccae', '#828ff6', '#f470f0', '#93d7fe'];
const gradientColors: [string, string][] = [
  ['#409eff', '#79bbff'],
  ['#f56c6c', '#fab6b6'],
  ['#e6a23c', '#eebe77'],
  ['#67c23a', '#95d475'],
  ['#fe68f2', '#f9a5b9'],
  ['#00ccae', '#6addc5'],
  ['#828ff6', '#70c8f4'],
  ['#f470f0', '#dca3e6'],
  ['#93d7fe', '#6d99f5'],
];

const axisLine = () => ({ lineStyle: {} });

const axisLabel = () => ({ margin: 20 });

const axisTick = () => ({ show: false });

const legend = () => ({ icon: 'circle', itemGap: 15, itemWidth: 12, textStyle: { fontSize: 12 } });

const grid = () => ({ top: 38, left: 10, right: 20, bottom: 0, containLabel: true });

const getXAxis = (): XAXisOption => ({ type: 'category', boundaryGap: false, axisLine: axisLine(), axisLabel: axisLabel(), axisTick: axisTick() });

const getColor = (_colors: string | [string, string], location: _$Location) => {
  if (Array.isArray(_colors)) {
    return _colors.length > 1 ? getLinearGradient([_colors[0], _colors[1]], location) : _colors[0] || '';
  } else {
    return _colors;
  }
};

const yAxis = (): YAXisOption => ({
  type: 'value',
  splitLine: { show: true, lineStyle: { type: 'dashed' } },
  axisLabel: {
    formatter: (val: number) => {
      let v = '';
      if (!isNaN(val) && Math.abs(val) >= 1000) {
        v = `${(Number(val) / 1000).toFixed(1)}K`;
      } else if (!isNaN(val) && Math.abs(val) >= 100) {
        v = Number(val).toFixed(1);
      } else {
        v = val.toFixed(2);
      }
      return v;
    },
  },
});

/**
 * 仪表盘
 */
export function gaugeChart(data: GaugeSeriesOption['data'], color: string, bj?: string): _$GaugeECOption {
  const background: string = bj || hexToRgba(color, 0.15) || colors[0]!;
  if (data) {
    data.forEach((item) => {
      if (typeof item === 'object' && 'value' in item) {
        item.value = Number(item.value) * 10000;
      }
    });
  }
  const series: _$GaugeECOption['series'] = {
    type: 'gauge',
    radius: '85%',
    legendHoverLink: false,
    max: 10000,
    pointer: { show: false },
    progress: { show: true, overlap: false, roundCap: true, clip: false },
    axisLine: { roundCap: true, lineStyle: { width: 12, color: [[1, background]] } },
    splitLine: { show: false },
    axisTick: { show: false },
    axisLabel: { show: false },
    data,
    title: { fontSize: 12, offsetCenter: ['0%', '30%'] },
    itemStyle: { color },
    detail: {
      valueAnimation: true,
      width: '60%',
      lineHeight: 40,
      fontSize: 20,
      fontWeight: 'bolder',
      color,
      offsetCenter: ['0%', '-10%'],
      formatter: (val: number) => `${Math.trunc(val) / 100}%`,
    },
  };
  const option: _$GaugeECOption = { series: [series] };
  return option;
}

/**
 * 区域图(折线图的变体)
 */
export function areaChart(optionData: _$LineECOption): _$LineECOption {
  const series: LineSeriesOption[] = [];
  (Array.isArray(optionData.series) ? optionData.series : []).forEach((item, index: number) => {
    const seriesItem: LineSeriesOption = {
      legendHoverLink: false,
      type: 'line',
      smooth: true,
      lineStyle: { width: 1 },
      showSymbol: true, // 直接设置false在5.3版本中会有警告 ,所以这里设置true但symbolSize设置为0
      symbolSize: 0,
      emphasis: { focus: 'series' },
      areaStyle: { opacity: 0.7 },
      ...item,
    };
    const cor = (item.color || [hexToRgba(gradientColors[index]![0], 0.1), gradientColors[index]![1]]) as string | [string, string] | undefined;
    if (cor && seriesItem.areaStyle) {
      seriesItem.areaStyle.color = getColor(cor, [0, 1, 0, 0]);
    }
    series.push(seriesItem);
  });
  const tooltip: _$LineECOption['tooltip'] = { trigger: 'axis', textStyle: { fontSize: 12 } };
  const xAxis: _$LineECOption['xAxis'] = { ...getXAxis(), axisLine: { show: false }, ...optionData.xAxis };
  const option: _$LineECOption = { color: colors, tooltip, legend: legend(), grid: grid(), xAxis, yAxis: yAxis(), series };
  return option;
}

/**
 * 柱状图
 */
export function barChart(optionData: _$BarECOption): _$BarECOption {
  const series: BarSeriesOption[] = [];
  (Array.isArray(optionData.series) ? optionData.series : []).forEach((item, index) => {
    const seriesItem: BarSeriesOption = {
      type: 'bar',
      barWidth: 10,
      legendHoverLink: false,
      itemStyle: { borderRadius: [4, 4, 4, 4], shadowColor: 'rgba(0, 0, 0, 0.1)', shadowBlur: 20 },
      ...item,
    };
    const data = item.data || [];
    seriesItem.data = data.map((val) => {
      if (typeof val === 'string' || typeof val === 'number' || val instanceof Date) {
        const ser: NonNullable<BarSeriesOption['data']>[number] = {
          value: val,
          itemStyle: { borderRadius: typeof val === 'number' && val > 0 ? [15, 15, 0, 0] : [0, 0, 15, 15] },
        };
        let cor = (item.color as string) || gradientColors[index]!;
        if (typeof val === 'number' && val < 0 && Array.isArray(cor) && cor.length >= 2) {
          cor = [cor[1]!, cor[0]!];
        }
        if (cor && ser.itemStyle) {
          ser.itemStyle.color = getColor(cor, [1, 1, 0, 0]);
        }
        return ser;
      } else {
        return val;
      }
    });
    series.push(seriesItem);
  });
  const xAxis: _$LineECOption['xAxis'] = { ...getXAxis(), ...optionData.xAxis, axisLine: { show: false } };
  const myYAxis: _$LineECOption['yAxis'] = { ...yAxis(), axisLine: { show: false } };
  const tooltip: _$LineECOption['tooltip'] = { trigger: 'axis', textStyle: { fontSize: 12 } };
  const option: _$BarECOption = { color: colors, tooltip, legend: legend(), grid: grid(), xAxis, yAxis: myYAxis, series };
  return option;
}

/**
 * 折线图
 */
export function lineChart(optionData: _$LineECOption): _$LineECOption {
  const series: LineSeriesOption[] = [];
  (Array.isArray(optionData.series) ? optionData.series : []).forEach((item, index) => {
    const seriesItem: LineSeriesOption = {
      type: 'line',
      symbol: 'circle',
      smooth: true,
      legendHoverLink: false,
      showSymbol: true, // 直接设置false在5.3版本中会有警告 ,所以这里设置true但symbolSize设置为0
      symbolSize: 0,
      itemStyle: {},
      ...item,
    };
    const cor = (item.color || gradientColors[index]) as [string, string];
    if (cor && seriesItem.lineStyle) {
      seriesItem.lineStyle.color = getColor(cor, [1, 1, 0, 0]);
    }
    seriesItem.lineStyle = { width: 5, cap: 'round' };
    seriesItem.lineStyle.shadowColor = (item.color || hexToRgba(gradientColors[index]![0], 0.3)) as string; // 阴影颜色
    seriesItem.lineStyle.shadowBlur = 12; // 图形阴影的模糊大小(uniapp和纯vue渲染有一点区别)
    seriesItem.lineStyle.shadowOffsetX = -0; // 阴影偏移(uniapp和纯vue渲染有一点区别)
    seriesItem.lineStyle.shadowOffsetY = -14; // 阴影偏移(uniapp和纯vue渲染有一点区别)
    series.push(seriesItem);
  });
  const myYAxis: _$LineECOption['yAxis'] = {
    ...yAxis(),
    max: (value) => value.max + (value.max - value.min) / 4,
    splitLine: { show: false },
    axisLine: { show: false },
  };
  const xAxis: _$LineECOption['xAxis'] = { ...getXAxis(), axisLine: { show: false }, ...optionData.xAxis };
  const tooltip: _$LineECOption['tooltip'] = { trigger: 'axis', textStyle: { fontSize: 12 } };
  const option: _$LineECOption = { color: colors, tooltip, legend: legend(), grid: grid(), xAxis, yAxis: myYAxis, series };
  return option;
}

/**
 * 设置图表
 */
export function initMyEChart(domID: string, theme: $GlobalTheme, option: _$BarECOption | _$GaugeECOption | _$LineECOption) {
  const dom = document.getElementById(domID);
  if (dom) {
    const myChart: echarts.ECharts = echarts.init(dom, theme === 'dark' ? 'dark' : undefined);
    option.backgroundColor = 'rgba(0,0,0,0)'; // 设置背景色
    option && myChart.setOption(option);
    return myChart;
  } else {
    return undefined;
  }
}
